/****************************************************************************
  Copyright (C) 2010 Wanderson Regis Silva
  All rights reserved
  Contact: Wanderson Regis Silva (wanderson.regis@ymail.com)

  This file is part of OpenEditor project

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/

#include <qapplication.h>
#include <qdir.h>
#include <qfiledialog.h>
#include <qmdiarea.h>
#include <qmdisubwindow.h>
#include <qmessagebox.h>
#include <qmenubar.h>
#include <qsettings.h>

#include "codeedit.h"
#include "iconprovider.h"
#include "finddialog.h"
#include "mainwindow.h"
#include "preferencesdialog.h"
#include "visualedit.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), __findDialog(0), __countOpened(0), __mainArea(new QMdiArea) {
  setWindowTitle(qApp->applicationName());
  setMinimumSize(800, 600);

  QMenuBar *menu = menuBar();

  QMenu *file = menu->addMenu(tr("File"));
  file->addAction(tr("New"), this, SLOT(newAct()), QKeySequence(Qt::CTRL + Qt::Key_N));
  file->addAction(tr("Open"), this, SLOT(openAct()), QKeySequence(Qt::CTRL + Qt::Key_O));
  //QMenu *recent = file->addMenu(tr("Open Recent"));
  __close = file->addAction(tr("Close"), this, SLOT(closeAct()), QKeySequence(Qt::CTRL + Qt::Key_W));
  file->addSeparator();
  __save = file->addAction(tr("Save"), this, SLOT(saveAct()), QKeySequence(Qt::CTRL + Qt::Key_S));
  __saveAs = file->addAction(tr("Save As"), this, SLOT(saveAsAct()), QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_S));
  __revert = file->addAction(tr("Revert"), this, SLOT(revertAct()), QKeySequence(Qt::ALT + Qt::Key_F5));
  file->addSeparator();
  file->addAction(tr("Exit"), this, SLOT(close()), QKeySequence(Qt::CTRL + Qt::Key_Q));

  QMenu *edit = menu->addMenu(tr("Edit"));
  __undo = edit->addAction(tr("Undo"), this, SLOT(undoAct()), QKeySequence(Qt::CTRL + Qt::Key_Z));
  __redo = edit->addAction(tr("Redo"), this, SLOT(redoAct()), QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z));
  edit->addSeparator();
  __cut = edit->addAction(tr("Cut"), this, SLOT(cutAct()), QKeySequence(Qt::CTRL + Qt::Key_X));
  __copy = edit->addAction(tr("Copy"), this, SLOT(copyAct()), QKeySequence(Qt::CTRL + Qt::Key_C));
  __paste = edit->addAction(tr("Paste"), this, SLOT(pasteAct()), QKeySequence(Qt::CTRL + Qt::Key_V));
  edit->addSeparator();
  __selectAll = edit->addAction(tr("Select All"), this, SLOT(selectAllAct()), QKeySequence(Qt::CTRL + Qt::Key_A));
  edit->addSeparator();
  __findAndReplace = edit->addAction(tr("Find and Replace"), this, SLOT(findReplaceAct()), QKeySequence(Qt::CTRL + Qt::Key_F));
  edit->addSeparator();
  edit->addAction(tr("Preferences"), this, SLOT(preferencesAct()));

  __wndwMenu = menu->addMenu(tr("Window"));
  QAction *fullScreen = __wndwMenu->addAction(tr("Full Screen"));
    fullScreen->setCheckable(true);
    fullScreen->setChecked(false);
    fullScreen->setShortcut(QKeySequence(Qt::Key_F11));
    connect(fullScreen, SIGNAL(triggered(bool)), this, SLOT(fullScreenAct(bool)));
  __wndwMenu->addSeparator();
  __openedGrp = new QActionGroup(this);

  QMenu *help = menu->addMenu(tr("Help"));
  help->addAction(tr("About"), this, SLOT(aboutAct()));

  /////////////////////////////
  __mainArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  __mainArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  setCentralWidget(__mainArea);
  connect(__mainArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(editorActivated(QMdiSubWindow*)));
  editorActivated(0);

  /////////////////////////////
  QSettings settings;
  if(settings.contains("openEditorState")) {
    restoreState(settings.value("openEditorState").toByteArray());
  }
}

void MainWindow::closeEvent(QCloseEvent *event) {
  __mainArea->closeAllSubWindows();

  if(__mainArea->currentSubWindow()) {
    event->ignore();
  } else {
    event->accept();
  }

  QSettings().setValue("openEditorState", saveState());
}

void MainWindow::newAct(void) {
  QString doc = tr("Unsaved Document");

  CodeEdit *c = new CodeEdit;
  c->loadFile(":htmlmodel");
  c->setFileName(QString());

  QMdiSubWindow *sub = __mainArea->addSubWindow(c);
  if( ! __mainArea->currentSubWindow())
    sub->setWindowState(Qt::WindowMaximized);
  sub->setWindowTitle(doc);
  __opened[__countOpened] = sub;

  QAction *act = __wndwMenu->addAction(doc, this, SLOT(showOpened()));
  __openedGrp->addAction(act);
  act->setCheckable(true);
  act->setChecked(true);
  act->setData(__countOpened++);

  sub->show();
}

void MainWindow::openAct(void) {
  QString fileTypes = tr("All files (*);;") +
                      tr("HTML files (*.htm *.html);;") +
                      tr("Javascript files (*.js);;") +
                      tr("PHP Files (*.php *.phtml *.phps);;") +
                      tr("Text files (*.txt);;") +
                      tr("XML files (*.xml *.xhtm *.xhtml);;") +
                      tr("CSS files (*.css)");

  QString fileName = QFileDialog::getOpenFileName(this, tr("Open File or Project"), QDir::homePath(), fileTypes);

  if( ! fileName.isEmpty()) {
    CodeEdit *c = new CodeEdit;

    if(c->loadFile(fileName)) {
      QString shortFileName = QFileInfo(fileName).fileName();
      c->setFileName(fileName);

      QMdiSubWindow *sub = __mainArea->addSubWindow(c);
      if( ! __mainArea->currentSubWindow())
        sub->setWindowState(Qt::WindowMaximized);

      QString suffix = QFileInfo(fileName).suffix();
      if(suffix == "htm" || suffix == "html")
        sub->setWindowIcon(IconProvider::getIcon("text-html"));
      else if(suffix == "css")
        sub->setWindowIcon(IconProvider::getIcon("text-css"));
      else
        sub->setWindowIcon(IconProvider::getIcon("text-plain"));


      sub->setWindowTitle(shortFileName);
      __opened[__countOpened] = sub;

      QAction *act = __wndwMenu->addAction(shortFileName, this, SLOT(showOpened()));
      __openedGrp->addAction(act);
      act->setCheckable(true);
      act->setChecked(true);
      act->setData(__countOpened++);

      sub->show();
    } else {
      QMessageBox::warning(this, qApp->applicationName(), tr("Cannot read file %1.").arg(fileName));
    }
  }
}

void MainWindow::closeAct(void) {
  __mainArea->closeActiveSubWindow();
}

void MainWindow::saveAct(void) {
  QMdiSubWindow *subWindow = __mainArea->currentSubWindow();
  CodeEdit *edit = dynamic_cast<CodeEdit*>(subWindow->widget());

  if(edit) {
    if(edit->fileName().isEmpty()) {
      QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"));

      if( ! fileName.isEmpty()) {
        if(edit->saveFile(fileName))
          subWindow->setWindowTitle(QFileInfo(edit->fileName()).fileName());
      }
    } else
      if(edit->saveFile())
        subWindow->setWindowTitle(QFileInfo(edit->fileName()).fileName());
  }
}

void MainWindow::saveAsAct(void) {
  QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"));

  if( ! fileName.isEmpty()) {
    QMdiSubWindow *subWindow = __mainArea->currentSubWindow();
    CodeEdit *edit = dynamic_cast<CodeEdit*>(subWindow->widget());

    if(edit->saveFile(fileName))
      subWindow->setWindowTitle(QFileInfo(edit->fileName()).fileName());
  }
}

void MainWindow::revertAct(void) {
  CodeEdit *edit = dynamic_cast<CodeEdit*>(__mainArea->currentSubWindow()->widget());

  if(edit)
    edit->revert();
}

void MainWindow::undoAct(void) {
  CodeEdit *edit = dynamic_cast<CodeEdit*>(__mainArea->currentSubWindow()->widget());

  if(edit)
    edit->undo();
}

void MainWindow::redoAct(void) {
  CodeEdit *edit = dynamic_cast<CodeEdit*>(__mainArea->currentSubWindow()->widget());

  if(edit)
    edit->redo();
}

void MainWindow::cutAct(void) {
  CodeEdit *edit = dynamic_cast<CodeEdit*>(__mainArea->currentSubWindow()->widget());

  if(edit)
    edit->cut();
}

void MainWindow::copyAct(void) {
  CodeEdit *edit = dynamic_cast<CodeEdit*>(__mainArea->currentSubWindow()->widget());

  if(edit)
    edit->copy();
}

void MainWindow::pasteAct(void) {
  CodeEdit *edit = dynamic_cast<CodeEdit*>(__mainArea->currentSubWindow()->widget());

  if(edit)
    edit->paste();
}

void MainWindow::selectAllAct(void) {
  CodeEdit *edit = dynamic_cast<CodeEdit*>(__mainArea->currentSubWindow()->widget());

  if(edit)
    edit->selectAll();
}

void MainWindow::findReplaceAct() {
  if(__findDialog == 0)
    __findDialog = new FindDialog(this);
  else {
    if(__findDialog->isVisible())
      qApp->setActiveWindow(__findDialog);
    else {
      delete __findDialog;

      __findDialog = new FindDialog(this);
    }
  }

  __findDialog->setTarget(__mainArea->currentSubWindow()->widget());
  __findDialog->show();
}

void MainWindow::preferencesAct() {
  PreferencesDialog *preferences = new PreferencesDialog(this);

  preferences->show();
}

void MainWindow::fullScreenAct(const bool &checked) {
  if(checked)
    showFullScreen();
  else
    showNormal();
}

void MainWindow::aboutAct(void) {
  QMessageBox::about(this, qApp->applicationName(), tr("Text about OpenEdit..."));
}

void MainWindow::editorActivated(QMdiSubWindow *editor) {
  if(editor) {
    __close->setEnabled(true);
    __saveAs->setEnabled(true);
    __selectAll->setEnabled(true);
    __findAndReplace->setEnabled(true);

    CodeEdit *c = dynamic_cast<CodeEdit*>(editor->widget());
    if(c) {
      disconnect(this, SLOT(textChanged()));
      disconnect(this, SLOT(selectionChanged()));
      __paste->setEnabled(true);

      if(c->document()->isModified()) {
        __save->setEnabled(true);

        if(c->fileName().isEmpty())
          __revert->setDisabled(true);
        else
          __revert->setEnabled(true);
      } else {
        if(c->fileName().isEmpty())
          __save->setEnabled(true);
        else
          __save->setDisabled(true);

        __revert->setDisabled(true);
      }

      if(c->document()->isUndoAvailable()) __undo->setEnabled(true);
      else __undo->setDisabled(true);

      if(c->document()->isRedoAvailable()) __redo->setEnabled(true);
      else __redo->setDisabled(true);

      if(c->textCursor().selectedText().length()) {
        __cut->setEnabled(true);
        __copy->setEnabled(true);
      } else {
        __cut->setDisabled(true);
        __copy->setDisabled(true);
      }

      connect(c, SIGNAL(textChanged()), this, SLOT(textChanged()));
      connect(c, SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
    } else
      QMessageBox::warning(this, qApp->applicationName(), tr("Error on setting editor settings."));
  } else {
    __close->setDisabled(true);
    __save->setDisabled(true);
    __saveAs->setDisabled(true);
    __revert->setDisabled(true);
    __undo->setDisabled(true);
    __redo->setDisabled(true);
    __cut->setDisabled(true);
    __copy->setDisabled(true);
    __paste->setDisabled(true);
    __selectAll->setDisabled(true);
    __findAndReplace->setDisabled(true);
  }
}

void MainWindow::selectionChanged(void) {
  CodeEdit *c = dynamic_cast<CodeEdit*>(__mainArea->currentSubWindow()->widget());
  if(c) {
    if(c->textCursor().selectedText().length()) {
      __cut->setEnabled(true);
      __copy->setEnabled(true);
    } else {
      __cut->setDisabled(true);
      __copy->setDisabled(true);
    }
  }
}

void MainWindow::showOpened(void) {
  QAction *action = dynamic_cast<QAction*>(sender());

  __mainArea->setActiveSubWindow(__opened[action->data().toInt()]);
}

void MainWindow::textChanged(void) {
  QMdiSubWindow *subWindow = __mainArea->currentSubWindow();
  QWidget *editor = subWindow->widget();
  CodeEdit *c = dynamic_cast<CodeEdit*>(editor);
  if(c) {
    if(c->document()->isModified()) {
      __save->setEnabled(true);

      if(c->fileName().isEmpty()) {
        subWindow->setWindowTitle("Unsaved Document *");
      } else {
        __revert->setEnabled(true);

        subWindow->setWindowTitle(QFileInfo(c->fileName()).fileName() + " *");
      }
    } else {
      __save->setDisabled(true);
      __revert->setDisabled(true);

      if(c->fileName().isEmpty())
        subWindow->setWindowTitle("Unsaved Document");
      else
        subWindow->setWindowTitle(QFileInfo(c->fileName()).fileName());
    }

    if(c->document()->isUndoAvailable()) __undo->setEnabled(true);
    else __undo->setDisabled(true);

    if(c->document()->isRedoAvailable()) __redo->setEnabled(true);
    else __redo->setDisabled(true);
  } else {
    QMessageBox::warning(this, qApp->applicationName(), tr("Error on setting editor settings."));
  }
}
